﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using MvcClient.Models;

namespace MvcClient.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            return View();
        }

        [Authorize]
        public async Task<IActionResult> CallApi()
        {
            var client = new HttpClient();
            var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
            client.SetBearerToken(accessToken);
            var response = await client.GetAsync("http://localhost:5011/WeatherForecast");
            if (!response.IsSuccessStatusCode)
            {
                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    await RefreshToken();
                    return RedirectToAction();
                }

                throw new Exception(response.ReasonPhrase);
            }

            ViewData["Data"] = await response.Content.ReadAsStringAsync();
            return View();
        }

        [Authorize]
        public async Task<IActionResult> Privacy()
        {
            var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
            var idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);
            var refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
            ViewData["accessToken"] = accessToken;
            ViewData["idToken"] = idToken;
            ViewData["refreshToken"] = refreshToken;
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }

        public async Task LogOut()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignOutAsync("oidc");
        }

        public async Task<string> RefreshToken()
        {
            var client = new HttpClient();
            var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5000");
            var refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
            var response = await client.RequestRefreshTokenAsync(new RefreshTokenRequest()
            {
                ClientId = "mvc",
                ClientSecret = "secret",
                RefreshToken = refreshToken,
                Scope = "api1 openid profile",
                GrantType = OpenIdConnectGrantTypes.RefreshToken,
                Address = disco.TokenEndpoint
            });
            if (response.IsError)
            {
                throw new Exception(response.Error);
            }
            else
            {
                var expireAt = DateTime.UtcNow + TimeSpan.FromSeconds(response.ExpiresIn);
                var tokens = new[]
                {
                    new AuthenticationToken
                    {
                        Name=OpenIdConnectParameterNames.IdToken,
                        Value=response.IdentityToken
                    },
                    new AuthenticationToken
                    {
                        Name=OpenIdConnectParameterNames.AccessToken,
                        Value=response.AccessToken
                    },
                    new AuthenticationToken
                    {
                        Name=OpenIdConnectParameterNames.RefreshToken,
                        Value=response.RefreshToken
                    },
                    new AuthenticationToken
                    {
                        Name="expires_at",
                        Value=expireAt.ToString("O",CultureInfo.InvariantCulture)
                    }
                };

                var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
                result.Properties.StoreTokens(tokens);

                await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, result.Principal, result.Properties);
                return response.AccessToken;
            }
        }
    }
}
